import { removeArrayItem } from "../utils/array.util"
import aggregationTypeEnum from "../enum/aggregationType.enum"
import { phaseState } from "../enum/phase.enum"
import Monitor from "./Monitor"
import EngineContext from "../EngineContext"
export const ChangeAction = {
  added: "added",
  removed: "removed",
  updated: "updated"
}

export class ChangeRecord {
  index: string
  action: string
  diff: any
  constructor(index: string, action: string, diff: any) {
    this.index = index
    this.action = action
    this.diff = diff
  }
}
export class CollectionMonitor {
  changedHandlers: any[]
  awaitingInvoke: boolean
  diffRecord: any[]
  targetMonitors: Record<string, Monitor>
  config: any
  updatePhaseSync: EngineContext
  triggerChangeHandlers: any
  constructor(config: any, updatePhaseSync?: EngineContext) {
    this.changedHandlers = []
    this.awaitingInvoke = !1
    this.diffRecord = []
    this.targetMonitors = {}
    this.triggerChangeHandlers = this.invokeChangeHandlers.bind(this)
    this.config = config
    this.updatePhaseSync = updatePhaseSync
    if (this.config.aggregationType === aggregationTypeEnum.NextFrame) {
      if (!updatePhaseSync) throw Error("UpdatePhaseSync required for NextFrame aggregation")
    } else if (this.config.aggregationType === aggregationTypeEnum.Duration) throw Error("Aggregation Type not implemented")
  }
  hasDiffRecord() {
    return this.diffRecord.length > 0
  }
  clearDiffRecord() {
    this.diffRecord = []
    for (const e in this.targetMonitors) {
      const t = this.targetMonitors[e]
      t.hasDiffRecord() && t.clearDiffRecord()
    }
  }
  onChanged(e) {
    this.changedHandlers.push(e)
  }
  removeOnChanged(e) {
    removeArrayItem(this.changedHandlers, e)
  }
  invokeCallbacks(e) {
    if (this.changedHandlers.length > 0) for (const t of this.changedHandlers) t(e)
  }
  invokeChangeHandlers(e) {
    switch (this.config.aggregationType) {
      case aggregationTypeEnum.Immediate:
        this.invokeCallbacks(e)
        break
      case aggregationTypeEnum.NextFrame:
        this.awaitingInvoke ||
          ((this.awaitingInvoke = !0),
          this.updatePhaseSync.after(phaseState.End).then(() => {
            this.awaitingInvoke = !1
            this.invokeCallbacks(e)
          }))
        break
      case aggregationTypeEnum.Manual:
    }
  }
}
